package com.redpacketcover.rc.util

import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.app.AppOpsManager
import android.app.usage.UsageStats
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.Signature
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.util.Log
import androidx.core.content.FileProvider
import java.io.File
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.*
import kotlin.experimental.and

object AppUtils {

    /**
     * Register the status of application changed listener.
     *
     * @param obj      The object.
     * @param listener The status of application changed listener
     */
    fun registerAppStatusChangedListener(
            obj: Any,
            listener: Utils.OnAppStatusChangedListener
    ) {
        Utils.getActivityLifecycle().addOnAppStatusChangedListener(obj, listener)
    }

    /**
     * Unregister the status of application changed listener.
     *
     * @param obj The object.
     */
    fun unregisterAppStatusChangedListener(obj: Any) {
        Utils.getActivityLifecycle().removeOnAppStatusChangedListener(obj)
    }

    /**
     * Install the app.
     *
     * Target APIs greater than 25 must hold
     * `<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />`
     *
     * @param filePath The path of file.
     */
    fun installApp(filePath: String) {
        installApp(
                getFileByPath(
                        filePath
                )
        )
    }

    /**
     * Install the app.
     *
     * Target APIs greater than 25 must hold
     * `<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />`
     *
     * @param file The file.
     */
    fun installApp(file: File?) {
        if (!isFileExists(file) || file == null) return
        Utils.getApp()
                .startActivity(getInstallAppIntent(file, true))
    }

    /**
     * Install the app.
     *
     * Target APIs greater than 25 must hold
     * `<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />`
     *
     * @param activity    The activity.
     * @param filePath    The path of file.
     * @param requestCode If &gt;= 0, this code will be returned in
     * onActivityResult() when the activity exits.
     */
    fun installApp(
            activity: Activity,
            filePath: String,
            requestCode: Int
    ) {
        installApp(
                activity,
                getFileByPath(filePath),
                requestCode
        )
    }

    /**
     * Install the app.
     *
     * Target APIs greater than 25 must hold
     * `<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />`
     *
     * @param activity    The activity.
     * @param file        The file.
     * @param requestCode If &gt;= 0, this code will be returned in
     * onActivityResult() when the activity exits.
     */
    fun installApp(
            activity: Activity,
            file: File?,
            requestCode: Int
    ) {
        if (!isFileExists(file) || file == null) return
        activity.startActivityForResult(getInstallAppIntent(file), requestCode)
    }

    /**
     * Install the app silently.
     *
     * Without root permission must hold
     * `android:sharedUserId="android.uid.shell"` and
     * `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
     *
     * @param filePath The path of file.
     * @return `true`: success<br></br>`false`: fail
     */
    fun installAppSilent(filePath: String): Boolean {
        return installAppSilent(
                getFileByPath(
                        filePath
                ), null
        )
    }

    /**
     * Install the app silently.
     *
     * Without root permission must hold
     * `android:sharedUserId="android.uid.shell"` and
     * `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
     *
     * @param file The file.
     * @return `true`: success<br></br>`false`: fail
     */
    fun installAppSilent(file: File): Boolean {
        return installAppSilent(file, null)
    }


    /**
     * Install the app silently.
     *
     * Without root permission must hold
     * `android:sharedUserId="android.uid.shell"` and
     * `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
     *
     * @param filePath The path of file.
     * @param params   The params of installation(e.g.,`-r`, `-s`).
     * @return `true`: success<br></br>`false`: fail
     */
    fun installAppSilent(filePath: String, params: String): Boolean {
        return installAppSilent(
                getFileByPath(
                        filePath
                ), params
        )
    }

    /**
     * Install the app silently.
     *
     * Without root permission must hold
     * `android:sharedUserId="android.uid.shell"` and
     * `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
     *
     * @param file   The file.
     * @param params The params of installation(e.g.,`-r`, `-s`).
     * @return `true`: success<br></br>`false`: fail
     */
    fun installAppSilent(file: File?, params: String?): Boolean {
        return installAppSilent(
                file,
                params,
                isDeviceRooted()
        )
    }

    /**
     * Install the app silently.
     *
     * Without root permission must hold
     * `android:sharedUserId="android.uid.shell"` and
     * `<uses-permission android:name="android.permission.INSTALL_PACKAGES" />`
     *
     * @param file     The file.
     * @param params   The params of installation(e.g.,`-r`, `-s`).
     * @param isRooted True to use root, false otherwise.
     * @return `true`: success<br></br>`false`: fail
     */
    fun installAppSilent(
            file: File?,
            params: String?,
            isRooted: Boolean
    ): Boolean {
        if (!isFileExists(file)) return false
        val filePath = '"'.toString() + file!!.absolutePath + '"'.toString()
        val command = ("LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm install " +
                (if (params == null) "" else "$params ")
                + filePath)
        val commandResult = ShellUtils.execCmd(command, isRooted)
        if (commandResult.successMsg != null && commandResult.successMsg.toLowerCase().contains("success")) {
            return true
        } else {
            Log.e(
                    "AppUtils", "installAppSilent successMsg: " + commandResult.successMsg +
                    ", errorMsg: " + commandResult.errorMsg
            )
            return false
        }
    }

    /**
     * Uninstall the app.
     *
     * @param packageName The name of the package.
     */
    fun uninstallApp(packageName: String) {
        if (isSpace(packageName)) return
        Utils.getApp()
                .startActivity(getUninstallAppIntent(packageName, true))
    }

    /**
     * Uninstall the app.
     *
     * @param activity    The activity.
     * @param packageName The name of the package.
     * @param requestCode If &gt;= 0, this code will be returned in
     * onActivityResult() when the activity exits.
     */
    fun uninstallApp(
            activity: Activity,
            packageName: String,
            requestCode: Int
    ) {
        if (isSpace(packageName)) return
        activity.startActivityForResult(getUninstallAppIntent(packageName), requestCode)
    }

    /**
     * Uninstall the app silently.
     *
     * Without root permission must hold
     * `android:sharedUserId="android.uid.shell"` and
     * `<uses-permission android:name="android.permission.DELETE_PACKAGES" />`
     *
     * @param packageName The name of the package.
     * @return `true`: success<br></br>`false`: fail
     */
    fun uninstallAppSilent(packageName: String): Boolean {
        return uninstallAppSilent(packageName, false)
    }

    /**
     * Uninstall the app silently.
     *
     * Without root permission must hold
     * `android:sharedUserId="android.uid.shell"` and
     * `<uses-permission android:name="android.permission.DELETE_PACKAGES" />`
     *
     * @param packageName The name of the package.
     * @param isKeepData  Is keep the data.
     * @return `true`: success<br></br>`false`: fail
     */
    fun uninstallAppSilent(packageName: String, isKeepData: Boolean): Boolean {
        return uninstallAppSilent(
                packageName,
                isKeepData,
                isDeviceRooted()
        )
    }

    /**
     * Uninstall the app silently.
     *
     * Without root permission must hold
     * `android:sharedUserId="android.uid.shell"` and
     * `<uses-permission android:name="android.permission.DELETE_PACKAGES" />`
     *
     * @param packageName The name of the package.
     * @param isKeepData  Is keep the data.
     * @param isRooted    True to use root, false otherwise.
     * @return `true`: success<br></br>`false`: fail
     */
    fun uninstallAppSilent(
            packageName: String,
            isKeepData: Boolean,
            isRooted: Boolean
    ): Boolean {
        if (isSpace(packageName)) return false
        val command = ("LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm uninstall "
                + (if (isKeepData) "-k " else "")
                + packageName)
        val commandResult = ShellUtils.execCmd(command, isRooted)
        if (commandResult.successMsg != null && commandResult.successMsg.toLowerCase().contains("success")) {
            return true
        } else {
            Log.e(
                    "AppUtils", "uninstallAppSilent successMsg: " + commandResult.successMsg +
                    ", errorMsg: " + commandResult.errorMsg
            )
            return false
        }
    }

    /**
     * Return whether the app is installed.
     *
     * @param packageName The name of the package.
     * @return `true`: yes<br></br>`false`: no
     */
    fun isAppInstalled(packageName: String): Boolean {
        val packageManager = Utils.getApp().packageManager
        try {
            return packageManager.getApplicationInfo(packageName, 0) != null
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return false
        }

    }

    /**
     * Return whether the application with root permission.
     *
     * @return `true`: yes<br></br>`false`: no
     */
    fun isAppRoot(): Boolean {
        val result = ShellUtils.execCmd("echo root", true)
        if (result.result === 0) return true
        if (result.errorMsg != null) {
            Log.d("AppUtils", "isAppRoot() called" + result.errorMsg)
        }
        return false
    }

    /**
     * Return whether it is a debug application.
     *
     * @return `true`: yes<br></br>`false`: no
     */
    fun isAppDebug(): Boolean {
        return isAppDebug(Utils.getApp().packageName)
    }

    /**
     * Return whether it is a debug application.
     *
     * @param packageName The name of the package.
     * @return `true`: yes<br></br>`false`: no
     */
    fun isAppDebug(packageName: String): Boolean {
        if (isSpace(packageName)) return false
        try {
            val pm = Utils.getApp().packageManager
            val ai = pm.getApplicationInfo(packageName, 0)
            return ai != null && ai.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return false
        }

    }

    /**
     * Return whether it is a system application.
     *
     * @return `true`: yes<br></br>`false`: no
     */
    fun isAppSystem(): Boolean {
        return isAppSystem(Utils.getApp().packageName)
    }

    /**
     * Return whether it is a system application.
     *
     * @param packageName The name of the package.
     * @return `true`: yes<br></br>`false`: no
     */
    fun isAppSystem(packageName: String): Boolean {
        if (isSpace(packageName)) return false
        try {
            val pm = Utils.getApp().packageManager
            val ai = pm.getApplicationInfo(packageName, 0)
            return ai != null && ai.flags and ApplicationInfo.FLAG_SYSTEM != 0
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return false
        }

    }

    /**
     * Return whether application is foreground.
     *
     * @return `true`: yes<br></br>`false`: no
     */
    fun isAppForeground(): Boolean {
        return Utils.isAppForeground()
    }

    /**
     * Return whether application is foreground.
     *
     * Target APIs greater than 21 must hold
     * `<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />`
     *
     * @param packageName The name of the package.
     * @return `true`: yes<br></br>`false`: no
     */
    fun isAppForeground(packageName: String): Boolean {
        return !isSpace(packageName) && packageName == getForegroundProcessName()
    }

    /**
     * Launch the application.
     *
     * @param packageName The name of the package.
     */
    fun launchApp(packageName: String) {
        if (isSpace(packageName)) return
        Utils.getApp()
                .startActivity(getLaunchAppIntent(packageName, true))
    }

    /**
     * Launch the application.
     *
     * @param activity    The activity.
     * @param packageName The name of the package.
     * @param requestCode If &gt;= 0, this code will be returned in
     * onActivityResult() when the activity exits.
     */
    fun launchApp(
            activity: Activity,
            packageName: String,
            requestCode: Int
    ) {
        if (isSpace(packageName)) return
        activity.startActivityForResult(getLaunchAppIntent(packageName), requestCode)
    }

    /**
     * Relaunch the application.
     */
    fun relaunchApp() {
        relaunchApp(false)
    }

    /**
     * Relaunch the application.
     *
     * @param isKillProcess True to kill the process, false otherwise.
     */
    fun relaunchApp(isKillProcess: Boolean) {
        val packageManager = Utils.getApp().packageManager
        val intent = packageManager.getLaunchIntentForPackage(Utils.getApp().packageName) ?: return
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        Utils.getApp().startActivity(intent)
        if (!isKillProcess) return
        android.os.Process.killProcess(android.os.Process.myPid())
        System.exit(0)
    }

    /**
     * Launch the application's details settings.
     */
    fun launchAppDetailsSettings() {
        launchAppDetailsSettings(Utils.getApp().packageName)
    }

    /**
     * Launch the application's details settings.
     *
     * @param packageName The name of the package.
     */
    fun launchAppDetailsSettings(packageName: String) {
        if (isSpace(packageName)) return
        val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
        intent.data = Uri.parse("package:$packageName")
        Utils.getApp().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
    }

    /**
     * Exit the application.
     */
    fun exitApp() {
        val activityList = Utils.getActivityList()
        for (i in activityList.indices.reversed()) {// remove from top
            val activity = activityList.get(i)
            // sActivityList remove the index activity at onActivityDestroyed
            activity.finish()
        }
        System.exit(0)
    }

    /**
     * Return the application's icon.
     *
     * @return the application's icon
     */
    fun getAppIcon(): Drawable? {
        return getAppIcon(Utils.getApp().packageName)
    }

    /**
     * Return the application's icon.
     *
     * @param packageName The name of the package.
     * @return the application's icon
     */
    fun getAppIcon(packageName: String): Drawable? {
        if (isSpace(packageName)) return null
        try {
            val pm = Utils.getApp().packageManager
            val pi = pm.getPackageInfo(packageName, 0)
            return if (pi == null) null else pi!!.applicationInfo.loadIcon(pm)
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return null
        }

    }

    /**
     * Return the application's package name.
     *
     * @return the application's package name
     */
    fun getAppPackageName(): String {
        return Utils.getApp().packageName
    }

    /**
     * Return the application's name.
     *
     * @return the application's name
     */
    fun getAppName(): String? {
        return getAppName(Utils.getApp().packageName)
    }

    /**
     * Return the application's name.
     *
     * @param packageName The name of the package.
     * @return the application's name
     */
    fun getAppName(packageName: String): String? {
        if (isSpace(packageName)) return ""
        try {
            val pm = Utils.getApp().packageManager
            val pi = pm.getPackageInfo(packageName, 0)
            return if (pi == null) null else pi!!.applicationInfo.loadLabel(pm).toString()
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return ""
        }

    }

    /**
     * Return the application's path.
     *
     * @return the application's path
     */
    fun getAppPath(): String? {
        return getAppPath(Utils.getApp().packageName)
    }

    /**
     * Return the application's path.
     *
     * @param packageName The name of the package.
     * @return the application's path
     */
    fun getAppPath(packageName: String): String? {
        if (isSpace(packageName)) return ""
        try {
            val pm = Utils.getApp().packageManager
            val pi = pm.getPackageInfo(packageName, 0)
            return if (pi == null) null else pi!!.applicationInfo.sourceDir
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return ""
        }

    }

    /**
     * Return the application's version name.
     *
     * @return the application's version name
     */
    fun getAppVersionName(): String? {
        return getAppVersionName(Utils.getApp().packageName)
    }

    /**
     * Return the application's version name.
     *
     * @param packageName The name of the package.
     * @return the application's version name
     */
    fun getAppVersionName(packageName: String): String? {
        if (isSpace(packageName)) return ""
        try {
            val pm = Utils.getApp().packageManager
            val pi = pm.getPackageInfo(packageName, 0)
            return if (pi == null) null else pi!!.versionName
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return ""
        }

    }

    /**
     * Return the application's version code.
     *
     * @return the application's version code
     */
    fun getAppVersionCode(): Int {
        return getAppVersionCode(Utils.getApp().packageName)
    }

    /**
     * Return the application's version code.
     *
     * @param packageName The name of the package.
     * @return the application's version code
     */
    fun getAppVersionCode(packageName: String): Int {
        if (isSpace(packageName)) return -1
        try {
            val pm = Utils.getApp().packageManager
            val pi = pm.getPackageInfo(packageName, 0)
            return if (pi == null) -1 else pi!!.versionCode
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return -1
        }

    }

    /**
     * Return the application's signature.
     *
     * @return the application's signature
     */
    fun getAppSignature(): Array<Signature>? {
        return getAppSignature(Utils.getApp().packageName)
    }

    /**
     * Return the application's signature.
     *
     * @param packageName The name of the package.
     * @return the application's signature
     */
    fun getAppSignature(packageName: String): Array<Signature>? {
        if (isSpace(packageName)) return null
        try {
            val pm = Utils.getApp().packageManager
            @SuppressLint("PackageManagerGetSignatures")
            val pi = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
            return if (pi == null) null else pi!!.signatures
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return null
        }

    }

    /**
     * Return the application's signature for SHA1 value.
     *
     * @return the application's signature for SHA1 value
     */
    fun getAppSignatureSHA1(): String {
        return getAppSignatureSHA1(Utils.getApp().packageName)
    }

    /**
     * Return the application's signature for SHA1 value.
     *
     * @param packageName The name of the package.
     * @return the application's signature for SHA1 value
     */
    fun getAppSignatureSHA1(packageName: String): String {
        return getAppSignatureHash(packageName, "SHA1")
    }

    /**
     * Return the application's signature for SHA256 value.
     *
     * @return the application's signature for SHA256 value
     */
    fun getAppSignatureSHA256(): String {
        return getAppSignatureSHA256(Utils.getApp().packageName)
    }

    /**
     * Return the application's signature for SHA256 value.
     *
     * @param packageName The name of the package.
     * @return the application's signature for SHA256 value
     */
    fun getAppSignatureSHA256(packageName: String): String {
        return getAppSignatureHash(packageName, "SHA256")
    }

    /**
     * Return the application's signature for MD5 value.
     *
     * @return the application's signature for MD5 value
     */
    fun getAppSignatureMD5(): String {
        return getAppSignatureMD5(Utils.getApp().packageName)
    }

    /**
     * Return the application's signature for MD5 value.
     *
     * @param packageName The name of the package.
     * @return the application's signature for MD5 value
     */
    fun getAppSignatureMD5(packageName: String): String {
        return getAppSignatureHash(packageName, "MD5")
    }

    private fun getAppSignatureHash(packageName: String, algorithm: String): String {
        if (isSpace(packageName)) return ""
        val signature = getAppSignature(packageName)
        return if (signature == null || signature.isEmpty()) "" else bytes2HexString(
                hashTemplate(
                        signature[0].toByteArray(),
                        algorithm
                )
        )
                .replace("(?<=[0-9A-F]{2})[0-9A-F]{2}".toRegex(), ":$0")
    }

    /**
     * Return the application's information.
     *
     *  * name of package
     *  * icon
     *  * name
     *  * path of package
     *  * version name
     *  * version code
     *  * is system
     *
     *
     * @return the application's information
     */
    fun getAppInfo(): AppInfo? {
        return getAppInfo(Utils.getApp().packageName)
    }

    /**
     * Return the application's information.
     *
     *  * name of package
     *  * icon
     *  * name
     *  * path of package
     *  * version name
     *  * version code
     *  * is system
     *
     *
     * @param packageName The name of the package.
     * @return the application's information
     */
    fun getAppInfo(packageName: String): AppInfo? {
        try {
            val pm = Utils.getApp().packageManager
            val pi = pm.getPackageInfo(packageName, 0)
            return getBean(pm, pi)
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
            return null
        }

    }

    /**
     * Return the applications' information.
     *
     * @return the applications' information
     */
    fun getAppsInfo(): List<AppInfo> {
        val list = ArrayList<AppInfo>()
        val pm = Utils.getApp().packageManager
        val installedPackages = pm.getInstalledPackages(0)
        for (pi in installedPackages) {
            val ai = getBean(pm, pi) ?: continue
            list.add(ai)
        }
        return list
    }

    /**
     * Return the application's package information.
     *
     * @return the application's package information
     */
    fun getApkInfo(apkFile: File?): AppInfo? {
        return if (apkFile == null || !apkFile.isFile || !apkFile.exists()) null else getApkInfo(
                apkFile.absolutePath
        )
    }

    /**
     * Return the application's package information.
     *
     * @return the application's package information
     */
    fun getApkInfo(apkFilePath: String): AppInfo? {
        if (isSpace(apkFilePath)) return null
        val pm = Utils.getApp().packageManager
        val pi = pm.getPackageArchiveInfo(apkFilePath, 0)
        val appInfo = pi!!.applicationInfo
        appInfo.sourceDir = apkFilePath
        appInfo.publicSourceDir = apkFilePath
        return getBean(pm, pi)
    }

    private fun getBean(pm: PackageManager?, pi: PackageInfo?): AppInfo? {
        if (pm == null || pi == null) return null
        val ai = pi.applicationInfo
        val packageName = pi.packageName
        val name = ai.loadLabel(pm).toString()
        val icon = ai.loadIcon(pm)
        val packagePath = ai.sourceDir
        val versionName = pi.versionName
        val versionCode = pi.versionCode
        val isSystem = ApplicationInfo.FLAG_SYSTEM and ai.flags != 0
        return AppInfo(
                packageName,
                name,
                icon,
                packagePath,
                versionName,
                versionCode,
                isSystem
        )
    }

    /**
     * The application's information.
     */
    class AppInfo(
            packageName: String, name: String, icon: Drawable, packagePath: String,
            versionName: String, versionCode: Int, isSystem: Boolean
    )

    ///////////////////////////////////////////////////////////////////////////
    // other utils methods
    ///////////////////////////////////////////////////////////////////////////

    private fun isFileExists(file: File?): Boolean {
        return file != null && file.exists()
    }

    private fun getFileByPath(filePath: String): File? {
        return if (isSpace(filePath)) null else File(filePath)
    }

    private fun isSpace(s: String?): Boolean {
        if (s == null) return true
        var i = 0
        val len = s.length
        while (i < len) {
            if (!Character.isWhitespace(s[i])) {
                return false
            }
            ++i
        }
        return true
    }

    private fun isDeviceRooted(): Boolean {
        val su = "su"
        val locations = arrayOf(
                "/system/bin/",
                "/system/xbin/",
                "/sbin/",
                "/system/sd/xbin/",
                "/system/bin/failsafe/",
                "/data/local/xbin/",
                "/data/local/bin/",
                "/data/local/",
                "/system/sbin/",
                "/usr/bin/",
                "/vendor/bin/"
        )
        for (location in locations) {
            if (File(location + su).exists()) {
                return true
            }
        }
        return false
    }

    private val HEX_DIGITS = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')

    private fun hashTemplate(data: ByteArray?, algorithm: String): ByteArray? {
        if (data == null || data.isEmpty()) return null
        return try {
            val md = MessageDigest.getInstance(algorithm)
            md.update(data)
            md.digest()
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
            null
        }

    }

    private fun bytes2HexString(bytes: ByteArray?): String {
        if (bytes == null) return ""
        val len = bytes.size
        if (len <= 0) return ""
        val ret = CharArray(len shl 1)
        var i = 0
        var j = 0
        while (i < len) {
            ret[j++] = HEX_DIGITS[bytes[i].toInt().shr(4) and 0x0f]
            ret[j++] = HEX_DIGITS[(bytes[i] and 0x0f).toInt()]
            i++
        }
        return String(ret)
    }

    private fun getInstallAppIntent(file: File): Intent {
        return getInstallAppIntent(file, false)
    }

    private fun getInstallAppIntent(file: File, isNewTask: Boolean): Intent {
        val intent = Intent(Intent.ACTION_VIEW)
        val data: Uri
        val type = "application/vnd.android.package-archive"
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            data = Uri.fromFile(file)
        } else {
            val authority = Utils.getApp().packageName + ".utilcode.provider"
            data = FileProvider.getUriForFile(Utils.getApp(), authority, file)
            intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
        }
        Utils.getApp()
                .grantUriPermission(Utils.getApp().packageName, data, Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.setDataAndType(data, type)
        return if (isNewTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) else intent
    }

    private fun getUninstallAppIntent(packageName: String): Intent {
        return getUninstallAppIntent(packageName, false)
    }

    private fun getUninstallAppIntent(packageName: String, isNewTask: Boolean): Intent {
        val intent = Intent(Intent.ACTION_DELETE)
        intent.data = Uri.parse("package:$packageName")
        return if (isNewTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) else intent
    }

    private fun getLaunchAppIntent(packageName: String): Intent? {
        return getLaunchAppIntent(packageName, false)
    }

    private fun getLaunchAppIntent(packageName: String, isNewTask: Boolean): Intent? {
        val intent = Utils.getApp().packageManager.getLaunchIntentForPackage(packageName) ?: return null
        return if (isNewTask) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) else intent
    }

    private fun getForegroundProcessName(): String? {
        val am = Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager

        val pInfo = am.runningAppProcesses
        if (pInfo != null && pInfo.size > 0) {
            for (aInfo in pInfo) {
                if (aInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                    return aInfo.processName
                }
            }
        }
        if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP) {
            val pm = Utils.getApp().packageManager
            val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
            val list = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
            Log.i("ProcessUtils", list.toString())
            if (list.size <= 0) {
                Log.i(
                        "ProcessUtils",
                        "getForegroundProcessName: noun of access to usage information."
                )
                return ""
            }
            try {// Access to usage information.
                val info = pm.getApplicationInfo(Utils.getApp().packageName, 0)
                val aom = Utils.getApp().getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager

                if (aom.checkOpNoThrow(
                                AppOpsManager.OPSTR_GET_USAGE_STATS,
                                info.uid,
                                info.packageName
                        ) != AppOpsManager.MODE_ALLOWED
                ) {
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                    Utils.getApp().startActivity(intent)
                }
                if (aom.checkOpNoThrow(
                                AppOpsManager.OPSTR_GET_USAGE_STATS,
                                info.uid,
                                info.packageName
                        ) != AppOpsManager.MODE_ALLOWED
                ) {
                    Log.i(
                            "ProcessUtils",
                            "getForegroundProcessName: refuse to device usage stats."
                    )
                    return ""
                }
                val usageStatsManager = Utils.getApp()
                        .getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
                var usageStatsList: List<UsageStats>? = null
                val endTime = System.currentTimeMillis()
                val beginTime = endTime - 86400000 * 7
                usageStatsList = usageStatsManager
                        .queryUsageStats(
                                UsageStatsManager.INTERVAL_BEST,
                                beginTime, endTime
                        )
                if (usageStatsList == null || usageStatsList.isEmpty()) return null
                var recentStats: UsageStats? = null
                for (usageStats in usageStatsList) {
                    if (recentStats == null || usageStats.lastTimeUsed > recentStats.lastTimeUsed) {
                        recentStats = usageStats
                    }
                }
                return recentStats?.packageName
            } catch (e: PackageManager.NameNotFoundException) {
                e.printStackTrace()
            }

        }
        return ""
    }

}